#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<assert.h> 
#include<string.h>

#define NUM 1024
#define OPT_NUM 64

char lineCommand [NUM];
char *myargv[OPT_NUM]; //指针数组，将控制台输入的字符串解析（切割）为一个一个选项 如："ls -a -l" -->  "ls" "-a" "-l"
int lastExitCode = 0;
int lastExitSignal = 0;


int main()
{
  while(1)
  {

    //打印输出提示符
    printf("[用户名@主机名 当前路径]#");
    fflush(stdout);
  
    //获取用户输入,你自己会输入\n,这样会让缓冲区里还剩下一个\n
    char* s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
    //除去缓冲区里的\n, abcd\n
    lineCommand[strlen(lineCommand)-1] = '\0';
    assert(s != NULL);
    (void)s;
 

    // "ls -a -l -i"  ->  "ls" "-a" "-l" "-i" //exec必须由数组或者列表进行传参
    // 将字符串切割为字串
    myargv[0] = strtok(lineCommand, " ");
    
    int i = 1;
    if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0)
    {
        myargv[i++]=(char*)"--color=auto";
    }
    
    //如果没有字串，strok会返回NULL，myargv[end]=NULL 
    while(myargv[i++] = strtok(NULL, " "));

    //cd命令无法更改目录，要进行修改
    //不需要创建子进程，直接让shell执行相应的命令，本质是执行系统接口
    //像这种，不需要我们的子进程来执行，而是让shell自己执行的命令叫做内置/内建命令
    if(myargv[0] != NULL && strcmp(myargv[0],"cd") == 0)
    {
      if(myargv[1] != NULL)
      {
        chdir(myargv[1]);
        //成功更改工作目录，直接重新执行shell
        //不能break
        continue;
      }
    }
    
    if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0],"echo") == 0 && strcmp(myargv[1],"$?") == 0)
    {
      if(strcmp(myargv[1], "$?") == 0)
      {
        printf("ecitcode:%d exitsignal:%d\n", lastExitCode, lastExitSignal);
      }
      else 
      {
        printf("%s\n",myargv[1]);
      }
      //无需继续执行
      continue;
    }

    #ifdef DEBUG 
    //测试是否成功切割
    for(int i = 0; myargv[i]; i++)
    {
      printf("myargv[%d]: %s\n", i, myargv[i]);
    }
    #endif

    //执行命令
    pid_t id = fork();
    //printf("%d\n",id);
    
    assert(id != -1);
    if(id == 0)
    {
      execvp(myargv[0],myargv);
      exit(1);
    }
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    assert(ret > 0);
    (void)ret;
    lastExitCode = ((status >> 8) & 0xFF);
    lastExitSignal = (status & 0x7F);
  }
  return 0;
}
